#import "MNNetwork.h"
#import <MJExtension/MJExtension.h>
#import <Foundation/Foundation.h>
#import <OpenSSLSDK/GMSm4Utils.h>
#import <CocoaSecurity/CocoaSecurity.h>
#import <ETSafe/ETSafe.h>
#import <OpenSSLSDK/GMSm3Utils.h>
#import "ETEnReq.h"

@interface MNNetwork ()

@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;

@property (nonatomic, strong) AFHTTPSessionManager *uploadManager;

@property (nonatomic, strong) AFHTTPSessionManager *downloadManager;


@property (nonatomic, assign) NSUInteger identifier;                //请求标识符

@property (nonatomic, strong) dispatch_semaphore_t sema;
@property (nonatomic, strong) dispatch_semaphore_t sema2;


@property (nonatomic, strong) ETWebViewController *webVC;
@end

@implementation MNNetwork

#pragma mark- ------ token ------
- (void)getAccessToken:(void(^)(BOOL success, MNBaseRsp *rsp))complete {
    ETApiTokenReq *req = ETApiTokenReq.alloc.init;
    req.waitUntilDone = YES;
    [self request:req callBack:^(__kindof MNBaseRsp * _Nonnull rsp) {
        if (rsp.responseObject[@"access_token"]) {
            self.accessToken = rsp.responseObject[@"access_token"];
            self.tokenType = rsp.responseObject[@"token_type"];
            NSString *expires_in = rsp.responseObject[@"expires_in"];
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((expires_in.intValue-20) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self getAccessToken:nil];
            });
            if (complete) {
                complete(YES, rsp);
            }
        }else{
            self.accessToken = nil;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [self getAccessToken:nil];
            });
            if (complete) {
                complete(NO, rsp);
            }
        }
    }];
}

- (void)dealloc
{
    [self cancelAllRequest];
}

+ (instancetype)shareInstance
{
    static MNNetwork *_instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[MNNetwork alloc] init];
//        [_instance getAccessToken:nil];
    });
    return _instance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.timeoutInterval = TIMEOUT_INTERVAL;
        self.identifier = 0;
        self.sema = dispatch_semaphore_create(1);
        self.sema2 = dispatch_semaphore_create(1);
        NSString *path = @"test.html";
            NSString *params = nil;

            //添加文件目录路径
            if (![path hasPrefix:@"etradekd_web/views/"]) {
                path = [NSString stringWithFormat:@"etradekd_web/views/%@",path];
            }
            
            NSString *urlPath = [NSBundle.mainBundle pathForResource:path ofType:@"" inDirectory:@""];
            DDLogInfo(@"打开html页面url:%@",urlPath);
            if (urlPath) {
                params = [NSString stringWithFormat:@"?%@",params];
                NSURL *url = [NSURL fileURLWithPath:urlPath];
                self.webVC = [ETWebViewController.alloc initWithUrl:[NSURL URLWithString:params?:nil relativeToURL:url]];
                self.webVC.view.backgroundColor = UIColor.whiteColor;
            }
    }
    return self;
}

- (AFHTTPSessionManager *)managerWith:(AFHTTPRequestSerializer *)requestSerializer {
    NSURLSessionConfiguration *conf = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    conf.requestCachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
    [conf setTLSMinimumSupportedProtocol:kTLSProtocol12];
    
    AFHTTPSessionManager *manager = [ETSafe maCls:@"AFHTTPSessionManager" pra1:[NSURL URLWithString:@"https://"] pra2:conf];
    //requestSerializer
    requestSerializer.timeoutInterval = self.timeoutInterval;
    manager.requestSerializer = requestSerializer;
    //responseSerializer
    AFJSONResponseSerializer *responseSerializer = [AFJSONResponseSerializer serializer];
    responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/plain", @"text/html", @"image/jpeg", nil];
    manager.responseSerializer = responseSerializer;
    
    //ssl
    NSData *data = [NSData dataWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"ssl" ofType:@"cer"]];
    if (data) {
        AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[NSSet setWithObject:data]];
        [policy setAllowInvalidCertificates:NO];
        policy.validatesDomainName = YES;
        manager.securityPolicy = policy;
    }else{
        AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
        policy.allowInvalidCertificates = YES;
        policy.validatesDomainName = NO;
        manager.securityPolicy = policy;
    }
    return manager;
}

- (AFHTTPSessionManager *)sessionManager {
    if (!_sessionManager) {
        AFJSONRequestSerializer *ser = [AFJSONRequestSerializer serializer];
        [ser setQueryStringSerializationWithBlock:^NSString * _Nullable(NSURLRequest * _Nonnull request, id  _Nonnull parameters, NSError *__autoreleasing  _Nullable * _Nullable error) {
            if ([parameters isKindOfClass:NSString.class]) {
                
            }
            NSLog(@"%@",parameters);
            return parameters;
        }];
        _sessionManager = [self managerWith:ser];
    }
    return _sessionManager;
}

- (AFHTTPSessionManager *)uploadManager {
    if (!_uploadManager) {
        _uploadManager = [self managerWith:[AFHTTPRequestSerializer serializer]];
    }
    return _uploadManager;
}

- (AFHTTPSessionManager *)downloadManager {
    if (!_downloadManager) {
        _downloadManager = [self managerWith:[AFJSONRequestSerializer serializer]];
    }
    return _downloadManager;
}

- (void)cancelAllRequest
{
    [_sessionManager invalidateSessionCancelingTasks:YES resetSession:YES];
    [_uploadManager invalidateSessionCancelingTasks:YES resetSession:YES];
    _sessionManager = nil;
    _uploadManager = nil;
}

- (void)cancelUploadRequest
{
    [_uploadManager invalidateSessionCancelingTasks:YES resetSession:YES];
    _uploadManager = nil;
}


- (NSMutableDictionary *)defaultHeaders {
    if (!_defaultHeaders) {
        _defaultHeaders = NSMutableDictionary.dictionary;
    }
    return _defaultHeaders;
}

#pragma mark 高度封装请求统一方法
- (void)request:(MNBaseReq *)req callBack:(nonnull void (^)(__kindof MNBaseRsp * _Nonnull))callBack downloadProgress:(nullable progressBlock)downloadProgress uploadProgress:(nullable progressBlock)uploadProgress {
    //获取token 检验有效性
    if (req.addToken) {
        BOOL tag = YES;
        if (ETUSERINFO.tokenList.count) {
            ETGetTokenListModel *model = ETUSERINFO.tokenList.firstObject;
            NSDate *date = [NSDate dateWithTimeIntervalSince1970:model.validEndTime/1000];
            NSTimeInterval sec = [NSDate.date timeIntervalSinceDate:date];
            if (sec < 0) {
                [req.httpHeaders setValue:ETUSERINFO.tokenList.lastObject.token forKey:@"X-Token"];
                [ETUSERINFO.tokenList removeLastObject];
            }else{
                tag = NO;
            }
        }else{
            tag = NO;
        }
        if (!tag) {
            [ETLoginInterface getTokenList:^(ETGetTokenListRsp * _Nonnull tokenRsp) {
                if (tokenRsp.isSuccess) {
                    ETUSERINFO.tokenList = [NSMutableArray arrayWithArray:tokenRsp.data];
                    [self request:req callBack:^(__kindof MNBaseRsp * _Nonnull rsp) {
                        callBack(rsp);
                    }];
                }else{
                    callBack(tokenRsp);
                }
            }];
            return;
        }
    }
    if (self.accessToken == nil && ![req isKindOfClass:ETApiTokenReq.class] && !ISXN) {
        [self getAccessToken:^(BOOL success, MNBaseRsp *tokenRsp) {
            if (success) {
                [self request:req callBack:^(__kindof MNBaseRsp * _Nonnull rsp) {
                    callBack(rsp);
                }];
            }else{
                MNBaseRsp *rsp = MNBaseRsp.alloc.init;
                rsp.code = -1;
                rsp.message = @"访问权限已过期，请稍后重试";
                rsp.status = -1;
                if (tokenRsp) {
                    rsp = tokenRsp;
                }
                if (callBack) {
                    callBack(rsp);
                }
            }
        }];
        return;
    }
    
    
    if (!req) {
        NSAssert(false, @"请求为空!");
        callBack(MNBaseRsp.alloc.init);
        return;
    }
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        [self lock];
        
        MNNetwork.shareInstance.identifier++;
        [req setValue:@(MNNetwork.shareInstance.identifier) forKey:@"identifier"];
        if (!req.waitUntilDone) {
            [self unlock];
        }
        
        NSMutableDictionary *param = [NSMutableDictionary dictionaryWithDictionary:req.mj_keyValues];
        if (req.paramsObject && [req.paramsObject isKindOfClass:NSDictionary.class]) {
            [param setValuesForKeysWithDictionary:req.paramsObject];
        }
        if ([req.paramsObject isKindOfClass:NSArray.class]) {
            param = req.paramsObject;
        }
        
        NSString *url = req.reqUrl;
        if (!url && req.routingPath) {
            url = [self.baseUrl stringByAppendingString:req.routingPath];
        }else if(!url){
            NSAssert(false, @"reqUrl is nil!");
        }
        if (req.routingParam) {
            url = [url stringByAppendingFormat:@"%@%@", [req.routingParam hasPrefix:@"/"]?@"":@"/", req.routingParam];
        }
        NSString *method = req.methodString;
        
        NSError *serializationError = nil;
        NSMutableURLRequest *request = nil;
        AFHTTPSessionManager *manager = nil;
//        if ([req.routingPath containsString:@"/px-common-authority/api/sso/getSmsCode"]) {
//            param = @{@"data":[GMSm4Utils ecbEncryptText:param.mj_JSONString key:@"bee983fc377a210d8a8c3beeea7b5abc"]};
//        }
        if (req.method == ReqMethodUpload && req.files.count > 0) {
            manager = self.uploadManager;
            request = [self.uploadManager.requestSerializer multipartFormRequestWithMethod:method URLString:url parameters:param constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
                [req.files enumerateObjectsUsingBlock:^(MNUploadFile * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                    [formData appendPartWithFileData:obj.data name:obj.name fileName:obj.fileName mimeType:obj.mimeType];
                }];
            } error:&serializationError];
        }else{
            manager = self.sessionManager;
            request = [self.sessionManager.requestSerializer requestWithMethod:method URLString:url parameters:param error:&serializationError];
        }
        
        //超时时间
        if (req.timeoutInterval > 0) {
            request.timeoutInterval = req.timeoutInterval;
        }else{
            request.timeoutInterval = self.timeoutInterval>0?self.timeoutInterval:TIMEOUT_INTERVAL;
        }
        [self.defaultHeaders enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            [request setValue:obj forHTTPHeaderField:key];
        }];
        [req.httpHeaders enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            [request setValue:obj forHTTPHeaderField:key];
        }];
        
//        NSString *p = nil;
//        NSString *b = nil;
        //params
//        if (param.count == 0) {
//            p = @"61da3ac42c0c7ebb5bc68652b1210c6e52391bcd5fb16ebc2f7bca36a46a523b";
//        }else{
//            p = [GMSm3Utils hashWithString:param.mj_JSONString];
//        }
//        p = @"61da3ac42c0c7ebb5bc68652b1210c6e52391bcd5fb16ebc2f7bca36a46a523b";
//        [request setValue:p.lowercaseString forHTTPHeaderField:@"P-Digest"];
//
//        //data
//        b =  [GMSm3Utils hashWithString:param.mj_JSONString];
//        [request setValue:b.lowercaseString forHTTPHeaderField:@"B-Digest"];
        
        if (serializationError) {
            
            DDLogDebug(@"MNNETWORK[%lu]-[%@] %@", (unsigned long)req.identifier, req.methodString, serializationError);
            if (callBack) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    MNBaseRsp *rsp = [[req.rspClass alloc] init];
                    if (req.isSB) {
                        rsp.status = [rsp.message isEqualToString:@"Success"]?0:-1;
                    }
                    if (rsp.successful) {
                        rsp.status = [rsp.successful isEqualToString:@"1"]?0:1;
                    }
                    rsp.error = serializationError;
                    rsp.identifier = req.identifier;
                    callBack(rsp);
                    if (req.waitUntilDone) {
                        [self unlock];
                    }
                });
            }else{
                if (req.waitUntilDone) {
                    [self unlock];
                }
            }
            return;
        }
        DDLogDebug(@"MNNETWORK[%lu]-[%@] %@%@ headers:%@",(unsigned long)req.identifier, method, url, param, request.allHTTPHeaderFields);
        
        
        if (!req.noEncrypt && !ISXN) {
            request = [ETEnReq en:request];
            //responseSerializer
//            AFJSONResponseSerializer *responseSerializer = [AFJSONResponseSerializer serializer];
            AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
            responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/plain", @"text/html", @"image/jpeg", nil];
            manager.responseSerializer = responseSerializer;
        }
        
        
        req.mn_startDate = NSDate.date;
        req.dataTask = [manager dataTaskWithRequest:request
                              uploadProgress:uploadProgress
                            downloadProgress:downloadProgress
                           completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
            NSTimeInterval t = [NSDate.date timeIntervalSinceDate:req.mn_startDate];
            if (error) {
                DDLogDebug(@"MNNETWORK[%lu][%0.2fs]-[RSP] %@", (unsigned long)req.identifier, t, error);
            }
//            DDLogDebug(@"MNNETWORK[%lu][%0.2fs]-[RSP] %@", (unsigned long)req.identifier, t, responseObject);
            
//            NSDictionary *dic = responseObject;
            
//            NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response;
            
            NSDictionary *dic = [self toJsonObject:responseObject response:(NSHTTPURLResponse *)response error:error req:req];
            [self resWithObj:dic error:error resp:(NSHTTPURLResponse *)response req:req callback:callBack];
        }];
        [req.dataTask resume];
    });
}

- (NSDictionary *)toJsonObject:(id)responseObject response:(NSHTTPURLResponse *)response error:(NSError *)error req:(MNBaseReq *)req {
    NSDictionary *dic = nil;
    if ([responseObject isKindOfClass:NSData.class]) { // data
        NSString *str = ((NSData *)responseObject).utf8String;
        dic = str.mj_JSONObject;
        // 要么是json string 要么是加密string
        if (dic == nil) {// 解密
            NSString *result = [GMSm4Utils cbcDecryptText:str key:@"f1b6d833fe6cddc37e5e0dda6fc0d878" IV:@"bb6654352d7e95c56895ba229b871996"];
            NSString *signature = response.allHeaderFields[@"signature"];
            NSString *body = [result substringFromIndex:signature.length];
            NSString *bodySignature = [result substringToIndex:signature.length];
            DDLogDebug(@"body明文:%@\nbodySignature:%@",result, bodySignature);
            if (![bodySignature isEqualToString:signature]) {
                dic = @{@"status":@-1,@"message":@"返回数据已被篡改"};
            }else{
                dic = body.mj_JSONObject;
            }
        }
    }else if ([responseObject isKindOfClass:NSDictionary.class] || [responseObject isKindOfClass:NSArray.class]) {// 本身字典
        dic = responseObject;
    }else if ([responseObject isKindOfClass:NSString.class]) { // json string
        dic = [(NSString *)responseObject mj_JSONObject];
    }else if(error){
        dic = @{@"status":@-1,@"message":error.localizedDescription};
    }else{
        dic = @{@"status":@-1,@"message":@"响应数据解析失败"};
    }
    if (!dic) {
        dic = @{@"status":@-1,@"message":@"响应数据解析失败"};
    }
    return dic;
}

- (void)resWithObj:(NSDictionary *)obj error:(NSError *)error resp:(NSHTTPURLResponse *)response req:(MNBaseReq *)req callback:(void(^)(MNBaseRsp *))callBack {
    NSTimeInterval t = [NSDate.date timeIntervalSinceDate:req.mn_startDate];
    DDLogDebug(@"MNNETWORK[%lu][%0.2fs]-[RSP] %@", (unsigned long)req.identifier, t, obj.mj_JSONObject);
    if (!callBack) {
        return;
    }
    MNBaseRsp *rsp = [req.rspClass mj_objectWithKeyValues:obj];
    if (!rsp || !obj) {
        rsp = [[req.rspClass alloc] init];
        if (error.code == -999) {
            rsp.code = -999;
            rsp.message = @"网络请求失败，请检查网络或稍后再试";
        }else{
            rsp.code = 1;
            rsp.message = error.localizedDescription?:@"网络请求失败，请检查网络或稍后再试";
        }
    }
    if (req.isSB) {
        rsp.status = [rsp.message isEqualToString:@"Success"]?0:-1;
    }
    if (rsp.successful) {
        rsp.status = [rsp.successful isEqualToString:@"1"]?0:1;
    }
    if (obj[@"error"]) {
        rsp.status = -1;
        rsp.message = rsp.message?:obj[@"error_description"]?:error.localizedDescription;
    }
    
    rsp.timeInterval = t;
    rsp.identifier = req.identifier;
    rsp.response = response;
    rsp.responseObject = obj;
    rsp.error = error;
    
    //401
    if (rsp.status == 401) {
        dispatch_async(dispatch_get_main_queue(), ^{
            UIViewController *vc = UIApplication.sharedApplication.keyWindow.rootViewController;
            if (![vc isKindOfClass:ETNavigationController.class]) {
                [ETUSERINFO clearInfo];
                [UIApplication.sharedApplication.keyWindow makeToast:@"会话超时，请重新登录"];
            }else{
                if (![vc.childViewControllers.lastObject isKindOfClass:ETLoginRegisterViewController.class]) {
                    callBack(rsp);
                }
            }
        });
        return;
    }else if(response.statusCode == 401) {
        [self getAccessToken:nil];
    }
    callBack(rsp);
    if (req.waitUntilDone) {
        [self unlock];
    }
}


- (void)request:(MNBaseReq *)req callBack:(nonnull void (^)(__kindof MNBaseRsp * _Nonnull))callBack {
    [self request:req callBack:callBack downloadProgress:nil uploadProgress:nil];
}

- (void)post:(NSString *)url params:(NSDictionary *)params callback:(void(^)(id rsp))callback {
    
}

#pragma mark- lock
- (void)lock {
    dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);
}

- (void)unlock {
    dispatch_semaphore_signal(self.sema);
}

- (void)lock2 {
    dispatch_semaphore_wait(self.sema2, DISPATCH_TIME_FOREVER);
}

- (void)unlock2 {
    dispatch_semaphore_signal(self.sema2);
}


@end
